﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Nature
{
    /// <summary>
    /// Represents a read-only array of elements
    /// </summary>
    /// <typeparam name="T">The type of elements in the array</typeparam>
    [DebuggerDisplay("Length: {Length}")]
    public struct ReadOnlyArray<T> : IRoughlyEquatable<ReadOnlyArray<T>, T>,
        IEnumerable<T>
    {
        #region Private Fields
        [DebuggerBrowsable(DebuggerBrowsableState.RootHidden | DebuggerBrowsableState.Never)]
        private T[] m_array;
        #endregion

        /// <summary>
        /// Initializes a new object of the ReadOnlyArray(T) data type incapsulating the given array of values
        /// </summary>
        /// <param name="a">The type of elements in the array</param>
        [DebuggerStepThrough()]
        public ReadOnlyArray(T[] a)
        {
            m_array = a;
        }

        /// <summary>
        /// Implicitly converts the given array of values to the object of the ReadOnlyArray(T) type
        /// </summary>
        /// <param name="a">Array of values</param>
        /// <returns>ReadOnlyArray masking the given array of values</returns>
        [DebuggerStepThrough()]
        public static implicit operator ReadOnlyArray<T>(T[] a)
        {
            return new ReadOnlyArray<T>(a);
        }

        /// <summary>
        /// Gets the value of the element at a specific position in the ReadOnlyArray
        /// </summary>
        /// <param name="i">The zero-based index of the value to get.</param>
        /// <returns>The value of the element at position index.</returns>
        [DebuggerHidden()]
        public T this[int i]
        {
            get { return m_array[i]; }
        }

        /// <summary>
        /// Gets a 32-bit integer that represents the total number of elements in all the dimensions of the System.Array.
        /// </summary>
        [DebuggerHidden()]
        public int Length
        {
            get
            {
                return m_array.Length;
            }
        }

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        internal T[] Data { get { return m_array; } }

        /// <summary>
        /// Finds the index of the given array item
        /// </summary>
        /// <param name="item">Array item</param>
        /// <returns>Index of the array item</returns>
        public int IndexOf(T item)
        {
            return Array.IndexOf(m_array, item);
        }

        /// <summary>
        /// Finds the index of the given array item
        /// </summary>
        /// <param name="pred">Seach criteria</param>
        /// <returns>Index of the array item</returns>                
        public int IndexOf(Func<T, bool> pred)
        {
            T item = m_array.FirstOrDefault(i => pred(i));            
            return Array.IndexOf(m_array, item);
        }

        /// <summary>
        /// Compares each elements of this array to each correspondent element of the other array
        /// </summary>
        /// <param name="other">Another array to compare with this object.</param>
        /// <returns>true if the current instance is equal to the other parameter; otherwise, false.</returns>
        public bool IsSame(ReadOnlyArray<T> other)
        {
            Debug.Assert(m_array.Length == other.Length);
            for (int i = 0; i < m_array.Length; ++i)
            {
                if (m_array[i].Equals(other.m_array[i]) == false) { return false; }
            }
            return true;
        }

        #region IEnumerable<T> Members

        /// <summary>
        /// Returns an System.Collections.Generic.IEnumerator(T) for the ReadOnlyArray.
        /// </summary>
        /// <returns>An System.CollectionsGeneric.IEnumerator(T) for the ReadOnlyArray.</returns>
        public IEnumerator<T> GetEnumerator()
        {
            for (int i = 0; i < m_array.Length; ++i)
            {
                yield return m_array[i];
            }
        }


        #endregion

        #region IEnumerable Members

        /// <summary>
        /// Returns an System.Collections.Generic.IEnumeratorfor the ReadOnlyArray.
        /// </summary>
        /// <returns>An System.Collections.Generic.IEnumerator for the ReadOnlyArray.</returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return m_array.GetEnumerator();
        }

        #endregion

        #region IRoughlyEquatable<ReadOnlyArray<T>,T> Members

        /// <summary>
        /// Indicates whether the current object is roughly equal to another object of the same type.
        /// </summary>
        /// <typeparam name="TTolerance">Generic tolerance type</typeparam>
        /// <param name="other">An object to compare with this object.</param>
        /// <param name="tolerance">A tolerance object determining the comparison accuracy</param>
        /// <returns>true if the current object is roughly equal to the other parameter; otherwise, false.</returns>
        public bool IsSame<TTolerance>(ReadOnlyArray<T> other, TTolerance tolerance) where TTolerance : ITolerance<T>
        {
            for (int i = 0; i < m_array.Length; ++i)
            {
                if (false == tolerance.AreSame(m_array[i], other.m_array[i]))
                {
                    return false;
                }
            }
            return true;
        }

        #endregion

    }


    public static partial class ITolerance
    {
        /// <summary>
        /// Copies content of this ReadOnlyArray instance into the target array
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">Source array (readonly)</param>
        /// <param name="target">Traget array</param>
        public static void CopyTo<T>(this ReadOnlyArray<T> source, T[] target)
        {
            Debug.Assert(source.Length == target.Length);
            for (int i = 0; i < source.Length; ++i) { target[i] = source[i]; }
        }

        /// <summary>
        /// Copies content of this ReadOnlyArray instance into the target array
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">Source array (readonly)</param>
        /// <param name="target">Traget array</param>
        public static void CopyTo(this ReadOnlyArray<double> source, decimal[] target)
        {
            Debug.Assert(source.Length == target.Length);
            for (int i = 0; i < source.Length; ++i) { target[i] = (decimal)source[i]; }
        }
    }

}


